#include "ohos_peer_connection_factory.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/create_peerconnection_factory.h"
#include "api/media_stream_interface.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
#include "api/audio/audio_processing.h"
#include "api/enable_media.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_event_log/rtc_event_log_factory.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "ohos_audio_source.h"
#include <api/task_queue/default_task_queue_factory.h>
#include "ohos_video_source.h"
#include "rtc_base/logging.h"
#include "api/scoped_refptr.h"
#include "api/make_ref_counted.h"
#include "api/transport/field_trial_based_config.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "rtc_base/thread.h"
#include "sdk/media_constraints.h"
#include "ohos_media_constraints.h"
#include "ohos_audio_track.h"
#include "ohos_video_track.h"
#include "ohos_media_stream.h"
#include "ohos_audio_source.h"
#include "ohos_video_source_adapter.h"
#include "pc/video_track_source_proxy.h"
#include "rtc_base/checks.h"
#include "ohos_peer_connection.h"

namespace ohoswebrtc {

OHOSPeerConnectionFactory::~OHOSPeerConnectionFactory() {
  RTC_LOG(LS_INFO) << __FUNCTION__ << ": Dtor";
}

rtc::scoped_refptr<OHOSPeerConnectionFactory> OHOSPeerConnectionFactory::Create() {
  return rtc::make_ref_counted<OHOSPeerConnectionFactory>();
}

OHOSPeerConnectionFactory::OHOSPeerConnectionFactory() {
  network_thread_ = rtc::Thread::CreateWithSocketServer();
  network_thread_->SetName("network_thread", nullptr);
  RTC_CHECK(network_thread_->Start()) << "Failed to start thread";

  worker_thread_ = rtc::Thread::Create();
  worker_thread_->SetName("worker_thread", nullptr);
  RTC_CHECK(worker_thread_->Start()) << "Failed to start thread";

  signaling_thread_ = rtc::Thread::Create();
  signaling_thread_->SetName("signaling_thread", nullptr);
  RTC_CHECK(signaling_thread_->Start()) << "Failed to start thread";
  
  std::unique_ptr<webrtc::FieldTrialsView> field_trials = std::make_unique<webrtc::FieldTrialBasedConfig>();
  task_queue_factory_ = webrtc::CreateDefaultTaskQueueFactory(field_trials.get());
  
  if (!peer_connection_factory_) {
    webrtc::PeerConnectionFactoryDependencies dependencies;
    dependencies.network_thread = network_thread_.get();
    dependencies.worker_thread = worker_thread_.get();
    dependencies.signaling_thread = signaling_thread_.get();

    dependencies.trials = std::make_unique<webrtc::FieldTrialBasedConfig>();
    dependencies.task_queue_factory = std::move(task_queue_factory_);
    dependencies.event_log_factory = std::make_unique<webrtc::RtcEventLogFactory>();

    if (network_thread_) {
      // Add an rtc::SocketFactory* argument.
      dependencies.socket_factory = network_thread_->socketserver();
    }
    // dependencies.adm = std::move(audio_device_module_); //adm这里不创建，直接通过webrtc内部创建，也可以在这里创建传递进去
    dependencies.audio_encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
    dependencies.audio_decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
    dependencies.audio_processing = webrtc::AudioProcessingBuilder().Create();
    dependencies.video_encoder_factory = webrtc::CreateBuiltinVideoEncoderFactory();
    dependencies.video_decoder_factory = webrtc::CreateBuiltinVideoDecoderFactory();
    webrtc::EnableMedia(dependencies);
    peer_connection_factory_ = CreateModularPeerConnectionFactory(std::move(dependencies));
    RTC_LOG(LS_INFO) << "PeerConnectionFactory created: " << peer_connection_factory_.get();
  }
}

rtc::scoped_refptr<OHOSPeerConnectionInterface> OHOSPeerConnectionFactory::CreatePeerConnection(
      webrtc::PeerConnectionInterface::RTCConfiguration& configuration,
      rtc::scoped_refptr<OHOSMediaConstraintsInterface> constraints,
      OHOSPeerConnectionObserver *observer) {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
      return signaling_thread_->BlockingCall([&] {
          return CreatePeerConnection_s(configuration, constraints, observer);
      });
  }
  return CreatePeerConnection_s(configuration, constraints, observer);
}

rtc::scoped_refptr<OHOSPeerConnectionInterface> OHOSPeerConnectionFactory::CreatePeerConnection_s(
      webrtc::PeerConnectionInterface::RTCConfiguration& configuration,
      rtc::scoped_refptr<OHOSMediaConstraintsInterface> constraints,
      OHOSPeerConnectionObserver *observer) {
  rtc::scoped_refptr<OHOSPeerConnectionInterface> peer_connection =
      rtc::scoped_refptr<OHOSPeerConnection>(
          new rtc::RefCountedObject<OHOSPeerConnection>(
              configuration, constraints, observer, rtc::scoped_refptr<OHOSPeerConnectionFactoryInterface>(this)));
  return peer_connection;
}

webrtc::RtpCapabilities OHOSPeerConnectionFactory::GetRtpSenderCapabilities(
      cricket::MediaType kind) const {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
    return signaling_thread_->BlockingCall([this, kind] {
      return peer_connection_factory_->GetRtpSenderCapabilities(kind);
    });
  }
  return peer_connection_factory_->GetRtpSenderCapabilities(kind);  
}

webrtc::RtpCapabilities OHOSPeerConnectionFactory::GetRtpReceiverCapabilities(
      cricket::MediaType kind) const {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
    return signaling_thread_->BlockingCall([this, kind] {
      return peer_connection_factory_->GetRtpReceiverCapabilities(kind);
    });
  }
  return peer_connection_factory_->GetRtpReceiverCapabilities(kind);     
}

rtc::scoped_refptr<OHOSAudioSourceInterface> OHOSPeerConnectionFactory::CreateAudioSource(
      rtc::scoped_refptr<OHOSMediaConstraintsInterface> constraints) {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
      return  signaling_thread_->BlockingCall([this, constraints] {
          return CreateAudioSource_s(constraints);
      });
  }
  return CreateAudioSource_s(constraints);
}

rtc::scoped_refptr<OHOSAudioTrackInterface> OHOSPeerConnectionFactory::CreateAudioTrack(
      rtc::scoped_refptr<OHOSAudioSourceInterface> source, const std::string track_id) {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
      return signaling_thread_->BlockingCall([this, source, track_id] {
          return CreateAudioTrack_s(source, track_id);
      });
  }
  return CreateAudioTrack_s(source, track_id);
}

rtc::scoped_refptr<OHOSVideoSourceInterface> OHOSPeerConnectionFactory::CreateVideoSource(const bool is_screen) {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
      return signaling_thread_->BlockingCall([this, is_screen] {
          return CreateVideoSource_s(is_screen);
        });
  }
  return CreateVideoSource_s(is_screen);
}

rtc::scoped_refptr<OHOSVideoTrackInterface> OHOSPeerConnectionFactory::CreateVideoTrack(
      rtc::scoped_refptr<OHOSVideoSourceInterface> source, const std::string track_id) {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
      return signaling_thread_->BlockingCall([this, source, track_id] {
        return CreateVideoTrack_s(source, track_id);
      });
  }
  return CreateVideoTrack_s(source, track_id);
}

rtc::scoped_refptr<OHOSMediaStreamInterface> OHOSPeerConnectionFactory::CreateLocalMediaStream(
      const std::string stream_id) {
  if (rtc::Thread::Current() != signaling_thread_.get()) {
    return signaling_thread_->BlockingCall([this, stream_id] {
      return CreateLocalMediaStream_s(stream_id);
    });
  }
  return CreateLocalMediaStream_s(stream_id);
}

rtc::scoped_refptr<OHOSAudioSourceInterface> OHOSPeerConnectionFactory::CreateAudioSource_s(
      rtc::scoped_refptr<OHOSMediaConstraintsInterface> constraints) {
  if (constraints) {
    cricket::AudioOptions options;
    webrtc::MediaConstraints::Constraints mandatory;
    webrtc::MediaConstraints::Constraints optional;
    webrtc::MediaConstraints *native_constraints = new webrtc::MediaConstraints(constraints->GetMandatory(), constraints->GetOptional());
    CopyConstraintsIntoAudioOptions(native_constraints, &options);
    rtc::scoped_refptr<webrtc::AudioSourceInterface> audio_source =
      peer_connection_factory_->CreateAudioSource(options);
    rtc::scoped_refptr<OHOSAudioSource> source = rtc::scoped_refptr<OHOSAudioSource>(
      new rtc::RefCountedObject<OHOSAudioSource>(audio_source, rtc::scoped_refptr<OHOSPeerConnectionFactoryInterface>(this)));
    return source;
  } else {
    rtc::scoped_refptr<webrtc::AudioSourceInterface> audio_source =
      peer_connection_factory_->CreateAudioSource(cricket::AudioOptions());
    rtc::scoped_refptr<OHOSAudioSource> source = rtc::scoped_refptr<OHOSAudioSource>(
      new rtc::RefCountedObject<OHOSAudioSource>(audio_source, rtc::scoped_refptr<OHOSPeerConnectionFactoryInterface>(this)));
    return source;
  }
}

rtc::scoped_refptr<OHOSAudioTrackInterface> OHOSPeerConnectionFactory::CreateAudioTrack_s(
      rtc::scoped_refptr<OHOSAudioSourceInterface> source, const std::string track_id) {
  OHOSAudioSource* audio_source =
      static_cast<OHOSAudioSource*>(source.get());

  rtc::scoped_refptr<webrtc::AudioTrackInterface> audio_track(
      peer_connection_factory_->CreateAudioTrack(
          track_id, audio_source->audio_source().get()));

  rtc::scoped_refptr<OHOSAudioTrack> track = rtc::scoped_refptr<OHOSAudioTrack>(
      new rtc::RefCountedObject<OHOSAudioTrack>(audio_track, rtc::scoped_refptr<OHOSPeerConnectionFactoryInterface>(this)));
  return track;
}

rtc::scoped_refptr<OHOSVideoSourceInterface> OHOSPeerConnectionFactory::CreateVideoSource_s(const bool is_screen_cast) {
  rtc::scoped_refptr<OHOSVideoTrackSourceAdapter> ohos_video_track_source =
      rtc::make_ref_counted<OHOSVideoTrackSourceAdapter>(is_screen_cast);
  rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> video_track_source = webrtc::VideoTrackSourceProxy::Create(signaling_thread_.get(), worker_thread_.get(), ohos_video_track_source);

  rtc::scoped_refptr<OHOSVideoSource> video_source = rtc::scoped_refptr<OHOSVideoSource>(
      new rtc::RefCountedObject<OHOSVideoSource>(video_track_source, rtc::scoped_refptr<OHOSPeerConnectionFactoryInterface>(this)));
  return video_source;
}

rtc::scoped_refptr<OHOSVideoTrackInterface> OHOSPeerConnectionFactory::CreateVideoTrack_s(
      rtc::scoped_refptr<OHOSVideoSourceInterface> source, const std::string track_id) {
  OHOSVideoSource* video_source =
      static_cast<OHOSVideoSource*>(source.get());
  rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track(
      peer_connection_factory_->CreateVideoTrack(video_source->video_source(), track_id));
  rtc::scoped_refptr<OHOSVideoTrack> track = rtc::scoped_refptr<OHOSVideoTrack>(
      new rtc::RefCountedObject<OHOSVideoTrack>(video_track, rtc::scoped_refptr<OHOSPeerConnectionFactoryInterface>(this)));
  return track;
}

void OHOSPeerConnectionFactory::CreateAudioDeviceModule_s() {
  audio_device_module_ = webrtc::AudioDeviceModule::Create(
        webrtc::AudioDeviceModule::kPlatformDefaultAudio,
        task_queue_factory_.get());
}

rtc::scoped_refptr<OHOSMediaStreamInterface> OHOSPeerConnectionFactory::CreateLocalMediaStream_s(
      const std::string stream_id) {
  rtc::scoped_refptr<webrtc::MediaStreamInterface> media_stream = peer_connection_factory_->CreateLocalMediaStream(stream_id);
  rtc::scoped_refptr<OHOSMediaStream> ohos_stream = rtc::scoped_refptr<OHOSMediaStream>(
      new rtc::RefCountedObject<OHOSMediaStream>(media_stream, rtc::scoped_refptr<OHOSPeerConnectionFactoryInterface>(this)));
  return ohos_stream;
}

}  // namespace ohoswebrtc